﻿using System;
using System.Collections.Generic;
using CloundUploader.Framework.Common;
using System.Collections.ObjectModel;

namespace CloundUploader.Framework.Upload
{

    public class UploaderManager
    {
        #region Singleton

        private static UploaderManager instance = new UploaderManager();

        public static UploaderManager Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Fields

        private List<Uploader> uploads = new List<Uploader>();
        private int addBatchCount;
        private ReaderWriterObjectLocker uploadListSync = new ReaderWriterObjectLocker();

        #endregion

        #region Properties

        public event EventHandler BeginAddBatchUploads;

        public event EventHandler EndAddBatchUploads;

        public event EventHandler<UploaderEventArgs> UploadEnded;

        public event EventHandler<UploaderEventArgs> UploadAdded;

        public event EventHandler<UploaderEventArgs> UploadRemoved;

        public ReadOnlyCollection<Uploader> Uploads
        {
            get
            {
                return uploads.AsReadOnly();
            }
        }

        public double TotalUploadRate
        {
            get
            {
                double total = 0;

                using (LockUploadList(false))
                {
                    for (int i = 0; i < this.Uploads.Count; i++)
                    {
                        if (this.Uploads[i].State == UploaderState.Working)
                        {
                            total += this.Uploads[i].Speed;
                        }
                    }
                }

                return total;
            }
        }

        public int AddBatchCount
        {
            get
            {
                return addBatchCount;
            }
        }

        #endregion

        #region Methods

        void uploads_StateChanged(object sender, EventArgs e)
        {
            Uploader uploader = (Uploader)sender;

            if (uploader.State == UploaderState.Ended ||
                uploader.State == UploaderState.EndedWithError)
            {
                OnUploadEnded((Uploader)sender);
            }
        }

        public IDisposable LockUploadList(bool lockForWrite)
        {
            if (lockForWrite)
            {
                return uploadListSync.LockForWrite();
            }
            else
            {
                return uploadListSync.LockForRead();
            }
        }

        public void RemoveUpload(int index)
        {
            RemoveUpload(Uploads[index]);
        }

        public void RemoveUpload(Uploader uploader)
        {
            if (uploader.State != UploaderState.NeedToPrepare ||
                uploader.State != UploaderState.Ended ||
                uploader.State != UploaderState.Paused)
            {
                uploader.Pause();
            }

            using (LockUploadList(true))
            {
                uploads.Remove(uploader);
            }

            OnUploadRemoved(uploader);
        }

        public void ClearEnded()
        {
            using (LockUploadList(true))
            {
                for (int i = uploads.Count - 1; i >= 0; i--)
                {
                    if (Uploads[i].State == UploaderState.Ended)
                    {
                        Uploader d = uploads[i];
                        uploads.RemoveAt(i);
                        OnUploadRemoved(d);
                    }
                }
            }
        }

        public void PauseAll()
        {
            using (LockUploadList(false))
            {
                for (int i = 0; i < this.Uploads.Count; i++)
                {
                    this.Uploads[i].Pause();
                }
            }
        }

        public Uploader Add(UploadFileInfo file)
        {
            Uploader up = new Uploader(file);
            up.StateChanged += new EventHandler(uploader_StateChanged);

            using (LockUploadList(true))
            {
                uploads.Add(up);
            }

            OnUploadAdded(up, false);

            return up;
        }
        void uploader_StateChanged(object sender, EventArgs e)
        {
            Uploader up = (Uploader)sender;

            if (up.State == UploaderState.Ended ||
                up.State == UploaderState.EndedWithError)
            {
                OnUploadEnded((Uploader)sender);
            }
        }
        protected virtual void OnUploadEnded(Uploader d)
        {
            if (UploadEnded != null)
            {
                UploadEnded(this, new UploaderEventArgs(d));
            }
        }


        public virtual void OnBeginAddBatchUploads()
        {
            addBatchCount++;

            if (BeginAddBatchUploads != null)
            {
                BeginAddBatchUploads(this, EventArgs.Empty);
            }
        }

        public virtual void OnEndAddBatchUploads()
        {
            addBatchCount--;

            if (EndAddBatchUploads != null)
            {
                EndAddBatchUploads(this, EventArgs.Empty);
            }
        }



        protected virtual void OnUploadAdded(Uploader up, bool willStart)
        {
            if (UploadAdded != null)
            {
                UploadAdded(this, new UploaderEventArgs(up, willStart));
            }
        }

        protected virtual void OnUploadRemoved(Uploader up)
        {
            if (UploadRemoved != null)
            {
                UploadRemoved(this, new UploaderEventArgs(up));
            }
        }

        public void SwapUploads(int idx, bool isThreadSafe)
        {
            if (isThreadSafe)
            {
                InternalSwap(idx);
            }
            else
            {
                using (LockUploadList(true))
                {
                    InternalSwap(idx);
                }
            }
        }

        private void InternalSwap(int idx)
        {
            if (this.uploads.Count <= idx)
            {
                //return;
            }

            Uploader it1 = this.uploads[idx];
            Uploader it2 = this.uploads[idx - 1];

            this.uploads.RemoveAt(idx);
            this.uploads.RemoveAt(idx - 1);

            this.uploads.Insert(idx - 1, it1);
            this.uploads.Insert(idx, it2);
        }
        #endregion
    }
}
